package com.wang.cloud.common.auth.handler;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import cn.dev33.satoken.exception.SameTokenInvalidException;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpStatus;
import com.wang.cloud.common.auth.exception.UserPasswordRetryLimitExceedException;
import com.wang.cloud.common.core.api.ApiResult;
import com.wang.cloud.common.core.api.ApiResultCode;
import com.wang.cloud.common.core.exception.BaseException;
import com.wang.cloud.common.core.exception.ServiceException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.NoSuchMessageException;
import org.springframework.core.annotation.Order;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;


/**
 * 全局异常处理器
 *
 * @author Lion Li
 */
@Order(2)
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 系统异常
     */
    @ExceptionHandler(Exception.class)
    public ApiResult<Void> handleException(Exception e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生系统异常.", requestURI, e);
        return ApiResult.failed(e.getMessage());
    }

    /**
     * 权限码异常
     */
    @ExceptionHandler(NotPermissionException.class)
    public ApiResult<Void> handleNotPermissionException(NotPermissionException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',权限码校验失败'{}'", requestURI, e.getMessage());
        return ApiResult.failed(HttpStatus.HTTP_FORBIDDEN, "没有访问权限，请联系管理员授权");
    }

    /**
     * 角色权限异常
     */
    @ExceptionHandler(NotRoleException.class)
    public ApiResult<Void> handleNotRoleException(NotRoleException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',角色权限校验失败'{}'", requestURI, e.getMessage());
        return ApiResult.failed(HttpStatus.HTTP_FORBIDDEN, "没有访问权限，请联系管理员授权");
    }

    /**
     * 认证失败
     */
    @ExceptionHandler(NotLoginException.class)
    public ApiResult<Void> handleNotLoginException(NotLoginException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI, e.getMessage());
        String type = e.getType();
        switch (type) {
            case NotLoginException.NOT_TOKEN:
                return ApiResult.failed(ApiResultCode.HTTP_UNAUTHORIZED, "请先登录");
            case NotLoginException.BE_REPLACED:
                return ApiResult.failed(ApiResultCode.BE_REPLACED, "已被顶下线");
            case NotLoginException.KICK_OUT:
                return ApiResult.failed(ApiResultCode.KICK_OUT, "已被踢下线");
            case NotLoginException.TOKEN_FREEZE:
            case NotLoginException.INVALID_TOKEN:
            case NotLoginException.NO_PREFIX:
            default:
                return ApiResult.failed(HttpStatus.HTTP_UNAUTHORIZED, "登录状态已失效，请重新登录");
        }
    }

    /**
     * 无效认证
     */
    @ExceptionHandler(SameTokenInvalidException.class)
    public ApiResult<Void> handleSameTokenInvalidException(SameTokenInvalidException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',内网认证失败'{}',无法访问系统资源", requestURI, e.getMessage());
        return ApiResult.failed(HttpStatus.HTTP_UNAUTHORIZED, "认证失败，无法访问系统资源");
    }

    /**
     * 请求方式不支持
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ApiResult<Void> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
                                                               HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
        return ApiResult.failed(e.getMessage());
    }

    /**
     * 业务异常
     */
    @ExceptionHandler(ServiceException.class)
    public ApiResult<Void> handleServiceException(ServiceException e, HttpServletRequest request) {
        log.error(e.getMessage());
        Integer code = e.getCode();
        return ObjectUtil.isNotNull(code) ? ApiResult.failed(code, e.getMessage()) : ApiResult.failed(e.getMessage());
    }

    /**
     * 业务异常
     */
    @ExceptionHandler(BaseException.class)
    public ApiResult<Void> handleBaseException(BaseException e, HttpServletRequest request) {
        return ApiResult.failed(e.getMessage());
    }

    /**
     * 请求路径中缺少必需的路径变量
     */
    @ExceptionHandler(MissingPathVariableException.class)
    public ApiResult<Void> handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI);
        return ApiResult.failed(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName()));
    }

    /**
     * 请求参数类型不匹配
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public ApiResult<Void> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI);
        return ApiResult.failed(String.format("请求参数类型不匹配，参数[%s]要求类型为：'%s'，但输入值为：'%s'", e.getName(), e.getRequiredType()
                .getName(), e.getValue()));
    }

    /**
     * 拦截未知的运行时异常
     */
    @ExceptionHandler(RuntimeException.class)
    public ApiResult<Void> handleRuntimeException(RuntimeException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生未知异常.", requestURI, e);
        return ApiResult.failed(e.getMessage());
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(BindException.class)
    public ApiResult<Void> handleBindException(BindException e) {
        log.error(e.getMessage());
        String message = e.getAllErrors()
                .get(0)
                .getDefaultMessage();
        return ApiResult.failed(message);
    }


    /**
     * 自定义验证异常
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public ApiResult<Void> handleException(ConstraintViolationException e) {
        for (ConstraintViolation<?> constraintViolation : e.getConstraintViolations()) {
            return ApiResult.failed(constraintViolation.getMessage());
        }
        return ApiResult.failed("参数错误");
    }

    /**
     * 自定义验证异常
     * 方法参数无效异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ApiResult<Void> handleException(MethodArgumentNotValidException e) {
        String message = e.getBindingResult()
                .getAllErrors()
                .get(0)
                .getDefaultMessage();
        return ApiResult.failed(message);
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(UserPasswordRetryLimitExceedException.class)
    public ApiResult<Void> handleException(UserPasswordRetryLimitExceedException e) {
        log.error("UserPasswordRetryLimitExceedException" + e.getMessage(), e);
        return ApiResult.failed(e.getMessage());
    }

    @ExceptionHandler(value = NoSuchMessageException.class)
    public ApiResult<Void> handleException(NoSuchMessageException e) {
        log.error("未配置错误提示 ", e);
        return ApiResult.failed("未配置错误提示");
    }

    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ApiResult<Void> handleException(HttpMessageNotReadableException e) {
        return ApiResult.failed(ApiResultCode.VALIDATE_FAILED, e.getMessage());
    }

    @ExceptionHandler(ServletRequestBindingException.class)
    public ApiResult<Void> handleException(ServletRequestBindingException e) {
        return ApiResult.failed(ApiResultCode.VALIDATE_FAILED, "缺少参数错误");
    }
}
